/**
 * ****************************************************************************
 * Copyright 2011, 2012 Chris Banes.
 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * *****************************************************************************
 */

package com.facebook.fresco.helper.photoview.photodraweeview;

import ohos.app.Context;
import ohos.multimodalinput.event.TouchEvent;

import com.alexvasilkov.gestures.ScaleGestureDetector;
import com.alexvasilkov.gestures.VelocityTracker;
import com.alexvasilkov.gestures.ViewConfiguration;

/**
 * 规模拖探测器
 *
 * @author dev
 * @since 2021-08-03
 */
public class ScaleDragDetector implements ScaleGestureDetector.OnScaleGestureListener {
    private static final int INVALID_POINTER_ID = -1;
    private VelocityTracker mVelocityTracker;
    private ScaleGestureDetector mScaleDetector;
    private OnScaleDragGestureListener mScaleDragGestureListener;
    private boolean mIsDragging;
    private float mLastTouchX;
    private float mLastTouchY;
    private int mTouchSlop;
    private int mMinimumVelocity;
    private int mActivePointerId = INVALID_POINTER_ID;
    private int mActivePointerIndex = 0;

    /**
     * 规模拖探测器
     *
     * @param context 上下文
     * @param scaleDragGestureListener 规模拖动手势侦听器
     */
    public ScaleDragDetector(Context context, OnScaleDragGestureListener scaleDragGestureListener) {
        mScaleDetector = new ScaleGestureDetector(context, this);
        mScaleDragGestureListener = scaleDragGestureListener;
        mMinimumVelocity = ViewConfiguration.getMinimumFlingVelocity();
        mTouchSlop = ViewConfiguration.getScaledTouchSlop();
    }


    @Override
    public boolean onScale(ScaleGestureDetector detector) {
        float scaleFactor = detector.getScaleFactor();

        if (Float.isNaN(scaleFactor) || Float.isInfinite(scaleFactor)) {
            return false;
        }

        mScaleDragGestureListener.onScale(scaleFactor, detector.getFocusX(), detector.getFocusY());
        return true;
    }

    @Override
    public boolean onScaleBegin(ScaleGestureDetector detector) {
        return true;
    }

    @Override
    public void onScaleEnd(ScaleGestureDetector detector) {
        mScaleDragGestureListener.onScaleEnd();
    }

    /**
     * 扩展
     *
     * @return boolean
     */
    public boolean isScaling() {
        return mScaleDetector.isInProgress();
    }

    /**
     * 拖着
     *
     * @return boolean
     */
    public boolean isDragging() {
        return mIsDragging;
    }

    private float getActiveX(TouchEvent ev) {
        if (mActivePointerIndex >= ev.getPointerCount()) {
            mActivePointerIndex = ev.getIndex();
        }
        return ev.getPointerScreenPosition(mActivePointerIndex).getX();
    }

    private float getActiveY(TouchEvent ev) {
        if (mActivePointerIndex >= ev.getPointerCount()) {
            mActivePointerIndex = ev.getIndex();
        }
        return ev.getPointerScreenPosition(mActivePointerIndex).getY();
    }

    /**
     * 触摸事件
     *
     * @param ev 电动汽车
     * @return boolean
     */
    public boolean onTouchEvent(TouchEvent ev) {
        mScaleDetector.onTouchEvent(ev);
        final int action = ev.getAction();
        onTouchActivePointer(action, ev);
        onTouchDragEvent(action, ev);
        return true;
    }

    /**
     * 触摸活跃的指针
     *
     * @param action 行动
     * @param ev 电动汽车
     */
    private void onTouchActivePointer(int action, TouchEvent ev) {
        switch (action) {
            case TouchEvent.PRIMARY_POINT_DOWN:
                mActivePointerId = ev.getPointerId(0);
                break;
            case TouchEvent.CANCEL:
            case TouchEvent.PRIMARY_POINT_UP:
                mActivePointerId = INVALID_POINTER_ID;
                break;
            case TouchEvent.OTHER_POINT_UP:
                int pointerIndex = ev.getIndex();
                int pointerId = ev.getPointerId(pointerIndex);
                if (pointerId == mActivePointerId) {
                    final int newPointerIndex = (pointerIndex == 0) ? 1 : 0;
                    mActivePointerId = ev.getPointerId(newPointerIndex);
                    mLastTouchX = ev.getPointerScreenPosition(newPointerIndex).getX();
                    mLastTouchY = ev.getPointerScreenPosition(newPointerIndex).getY();
                }
                break;
            default:
                break;
        }
        mActivePointerIndex = findPointerIndex(ev, mActivePointerId != INVALID_POINTER_ID ? mActivePointerId : 0);
    }

    /**
     * 在触摸拖动事件
     *
     * @param action 行动
     * @param ev 电动汽车
     */
    private void onTouchDragEvent(int action, TouchEvent ev) {
        switch (action) {
            case TouchEvent.PRIMARY_POINT_DOWN:
                if (mVelocityTracker == null) {
                    mVelocityTracker = new VelocityTracker();
                }
                mVelocityTracker.addMovement(ev);
                mLastTouchX = getActiveX(ev);
                mLastTouchY = getActiveY(ev);
                mIsDragging = false;
                break;
            case TouchEvent.POINT_MOVE:
                final float activeX = getActiveX(ev);
                final float activeY = getActiveY(ev);
                float dx = activeX - mLastTouchX;
                float dy = activeY - mLastTouchY;

                if (!mIsDragging) {
                    mIsDragging = Math.sqrt((dx * dx) + (dy * dy)) >= mTouchSlop;
                }
                if (mIsDragging) {
                    mScaleDragGestureListener.onDrag(dx, dy);
                    mLastTouchX = activeX;
                    mLastTouchY = activeY;
                    if (mVelocityTracker != null) {
                        mVelocityTracker.addMovement(ev);
                    }
                }
                break;
            case TouchEvent.CANCEL:
                if (null != mVelocityTracker) {
                    mVelocityTracker.recycle();
                    mVelocityTracker = null;
                }
                break;
            case TouchEvent.PRIMARY_POINT_UP:
                pointUp(ev);
                if (null != mVelocityTracker) {
                    mVelocityTracker.recycle();
                    mVelocityTracker = null;
                }
                break;
            default:
                break;
        }
    }

    private void pointUp(TouchEvent ev) {
        if (mIsDragging) {
            if (null != mVelocityTracker) {
                mLastTouchX = getActiveX(ev);
                mLastTouchY = getActiveY(ev);

                mVelocityTracker.addMovement(ev);
                mVelocityTracker.calculateCurrentVelocity(1000);

                float velocityX = mVelocityTracker.getXVelocity();
                float velocityY = mVelocityTracker.getYVelocity();

                if (Math.max(Math.abs(velocityX), Math.abs(velocityY)) >= mMinimumVelocity) {
                    mScaleDragGestureListener.onFling(mLastTouchX, mLastTouchY, -velocityX, -velocityY);
                }
            }
        }
    }

    private int findPointerIndex(TouchEvent ev, int id) {
        int idToBeFound = id != INVALID_POINTER_ID ? id : 0;
        for (int ii = 0; ii < ev.getPointerCount(); ii++) {
            if (ev.getPointerId(ii) == idToBeFound) {
                return ii;
            }
        }
        return ev.getIndex();
    }
}
